/* BenutzerErzeugung.java
 *
 * Author:	Malcolm Staudacher
 * Date:	May 1, 2008
 *
 */

package ocumed.applikation.domainhandling;

import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.Vector;

import ocumed.applikation.exceptions.BenutzerNotFoundException;
import ocumed.applikation.exceptions.NoArztException;
import ocumed.domaene.behandlung.DWarteposition;
import ocumed.domaene.behandlung.DWarteschlange;
import ocumed.domaene.benutzerverwaltung.DBenutzer;
import ocumed.domaene.benutzerverwaltung.DBenutzerGruppe;
import ocumed.domaene.benutzerverwaltung.arztverwaltung.DArzt;
import ocumed.domaene.benutzerverwaltung.arztverwaltung.DFehlzeit;
import ocumed.domaene.benutzerverwaltung.arztverwaltung.DOrdinationsZeit;
import ocumed.persistenz.dao.BenutzerDAO;
import ocumed.persistenz.dao.DAOFactory;
import ocumed.persistenz.dao.FehlzeitDAO;
import ocumed.persistenz.dao.GruppeDAO;
import ocumed.persistenz.dao.OrdinationDAO;
import ocumed.persistenz.dao.PatientDAO;
import ocumed.persistenz.dao.TerminDAO;
import ocumed.persistenz.dao.TerminartDAO;
import ocumed.persistenz.dao.WartepositionDAO;
import ocumed.persistenz.hibernate.OcBenutzer;
import ocumed.persistenz.hibernate.OcFehlzeit;
import ocumed.persistenz.hibernate.OcGruppe;
import ocumed.persistenz.hibernate.OcOrdination;
import ocumed.persistenz.hibernate.OcWarteposition;
import ocumed.teams.IArzt;
import ocumed.teams.IBenutzer;
import ocumed.teams.IPatient;
import ocumed.teams.ITermin;
import ocumed.teams.IWarteposition;
import ocumed.teams.IWarteschlange;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * provides functionality to create benutzer domain objects
 * 
 * @author Malcolm Staudacher
 * @author Daniel Rudigier
 * 
 */
public class BenutzerErzeugung {
	private static final Log log = LogFactory.getLog(BenutzerErzeugung.class);

	// access the domain handler
	private DomainHandler dh;

	// DAO interfaces
	protected BenutzerDAO m_benutzerDAO;
	protected GruppeDAO m_gruppeDAO;
	protected OrdinationDAO m_ordinationDAO;
	protected FehlzeitDAO m_fehlzeitDAO;
	protected TerminDAO m_terminDAO;
	protected TerminartDAO m_terminArtDAO;
	protected PatientDAO m_patientDAO;


	/**
	 * 
	 * constructor
	 * 
	 */
	public BenutzerErzeugung() {
		dh = DomainHandler.getInstance();

		m_fehlzeitDAO = DAOFactory.get(DAOFactory.RDBMS).getFehlzeitDAO();
		m_terminDAO = DAOFactory.get(DAOFactory.RDBMS).getTerminDAO();
		m_terminArtDAO = DAOFactory.get(DAOFactory.RDBMS).getTerminartDAO();
		m_benutzerDAO = DAOFactory.get(DAOFactory.RDBMS).getBenutzerDAO();
		m_patientDAO = DAOFactory.get(DAOFactory.RDBMS).getPatientDAO();
		m_ordinationDAO = DAOFactory.get(DAOFactory.RDBMS).getOrdinationDAO();
		m_gruppeDAO = DAOFactory.get(DAOFactory.RDBMS).getGruppeDAO();
	}


	/**
	 * Creates a DBenutzerGruppe domain object from the corresponding data
	 * object. The created object is registered with the domain handler.
	 * 
	 * @param ocGruppe
	 *            the data object.
	 * @return the domain object.
	 */
	private DBenutzerGruppe getDGruppe(OcGruppe ocGruppe) {
		m_gruppeDAO.start();
		DBenutzerGruppe bg = getGruppe(ocGruppe.getGruppeid());

		if (bg != null) {
			return bg;
		}

		int id = ocGruppe.getGruppeid();
		char typ = ocGruppe.getGruppetyp();
		String name = ocGruppe.getGruppename();

		DBenutzerGruppe gruppe = new DBenutzerGruppe(id, typ, name);
		dh.getGruppen().add(gruppe);

		return gruppe;
	}


	/**
	 * Creates all DBenutzerGruppe domain objects. All created objects are
	 * registered with the domain handler.
	 * 
	 * @return all DBenutzerGruppe domain objects
	 */
	private Vector<DBenutzerGruppe> getAllDGruppen() {
		log.debug("request all groups");

		m_gruppeDAO.start();
		List<OcGruppe> gruppen = m_gruppeDAO.findAll();

		Iterator<OcGruppe> iter = gruppen.iterator();
		OcGruppe cg = new OcGruppe();

		while (iter.hasNext()) {
			cg = iter.next();
			getDGruppe(cg);
		}

		return dh.getGruppen();
	}


	/**
	 * Creates a DBenutzer domain object from a OcBenutzer data object. The
	 * created object are registered with the domain handler.
	 * 
	 * @param ocArzt
	 *            The data object to to create the domain object from.
	 * @return the corresponding domain object.
	 */
	private DBenutzer getDBenutzer(OcBenutzer ocBenutzer) {
		m_benutzerDAO.start();
		m_gruppeDAO.start();
		int id = ocBenutzer.getBenutzerid();
		DBenutzer ben = getBenutzer(id);

		if (ben != null) {
			return ben;
		}

		DBenutzerGruppe gruppe = getDGruppe(ocBenutzer.getOcGruppe());
		String username = ocBenutzer.getBenutzerusername();
		String passwort = ocBenutzer.getBenutzerpasswort();
		String vorname = ocBenutzer.getBenutzervorname();
		String nachname = ocBenutzer.getBenutzernachname();
		char status = ocBenutzer.getBenutzerstatus();

		ben = new DBenutzer(id, gruppe, username, passwort, vorname, nachname,
				status);
		dh.getBenutzer().add(ben);
		return ben;
	}


	/**
	 * Creates DBenutzer domain objects for all OcBenutzer data objects. All
	 * created objects are registered with the domain handler.
	 * 
	 * @return a Vector containing all DBenutzer objects.
	 */
	private Vector<DBenutzer> getAllDBenutzer() {
		m_benutzerDAO.start();
		List<OcBenutzer> bl = m_benutzerDAO.findAll();
		Iterator<OcBenutzer> iter = bl.iterator();
		OcBenutzer cb = new OcBenutzer();

		while (iter.hasNext()) {
			cb = iter.next();
			getDBenutzer(cb);
		}

		return dh.getBenutzer();
	}
	
	
	public IBenutzer getIBenutzer(String username) {
		Vector<DBenutzer> allUsers = getAllDBenutzer();
		
		for (DBenutzer cb : allUsers) {
			
			if (cb.getBenutzerusername().equals(username)) {
				return (IBenutzer) cb;
			}
		}
		
		return null;
	}


	/**
	 * Creates a DFehlzeit domain object from the corresponding data object. The
	 * DBenutzer object referenced in the parameter object must be contained in
	 * the m_benutzer member variable of this class.
	 * 
	 * @param ocFehlzeit
	 *            the data object to create a domain object for.
	 * 
	 * @return the corresponding domain object.
	 * @throws BenutzerNotFoundException
	 *             if the DBenutzer object is not found in the m_benutzer field.
	 */
	private DFehlzeit getDFehlzeit(OcFehlzeit ocFehlzeit)
			throws BenutzerNotFoundException {
		DFehlzeit fz = getFehlzeit(ocFehlzeit.getFehlzeitid());

		if (fz != null) {
			return fz;
		}

		int i = 0;
		boolean found = false;
		DBenutzer benutzer = null;
		Calendar von = Calendar.getInstance();
		Calendar bis = Calendar.getInstance();

		// make sure DBenutzer object is already known
		for (i = 0; i < dh.getBenutzer().size() && found == false; i++) {
			if (ocFehlzeit.getOcBenutzer().getBenutzerid() == dh.getBenutzer()
					.elementAt(i).getBenutzerid()) {
				benutzer = dh.getBenutzer().elementAt(i);
				found = true;
			}
		}

		if (!found) {
			throw new BenutzerNotFoundException();
		}

		// TODO Methode reparieren
		von = ocFehlzeit.getFehlzeitvon();
		bis = ocFehlzeit.getFehlzeitbis();

		fz = new DFehlzeit(ocFehlzeit.getFehlzeitid(), benutzer, von, bis,
				ocFehlzeit.getFehlzeitgrund());
		dh.getFehlzeiten().add(fz);
		return fz;
	}


	/**
	 * Creates domain objects for all OcFehlzeit objects.
	 * 
	 * @return a Vector containing all Fehlzeit domain objects.
	 * @throws BenutzerNotFoundException
	 *             if a corresponding DBenutzer object cannot be found in the
	 *             m_benutzer field of this class.
	 */
	private Vector<DFehlzeit> getAllDFehlzeit()
			throws BenutzerNotFoundException {
		m_fehlzeitDAO.start();
		List<OcFehlzeit> fz = m_fehlzeitDAO.findAll();
		Iterator<OcFehlzeit> iter = fz.iterator();
		OcFehlzeit cfz = new OcFehlzeit();

		while (iter.hasNext()) {
			cfz = iter.next();
			getDFehlzeit(cfz);
		}

		return dh.getFehlzeiten();
	}


	/**
	 * Creates a DOrdinationsZeit domain object from the corresponding data
	 * object. If a domain object with the specified id already exists, that
	 * object is returned. The new object is registered with the domain handler.
	 * 
	 * @param ocOrdination
	 *            the data object.
	 * @return the corresponding domain object.
	 * @throws BenutzerNotFoundException
	 *             if the corresponding DBenutzer object is not found in the
	 *             m_benutzer field of this class.
	 */
	public DOrdinationsZeit getDOrdZeit(OcOrdination ocOrdination)
			throws BenutzerNotFoundException {
		int i = 0;
		boolean found = false;
		DBenutzer benutzer = null;
		Calendar anfang = Calendar.getInstance();
		Calendar ende = Calendar.getInstance();

		// check if domain object already exists
		DOrdinationsZeit oz = getOrdZeit(ocOrdination.getOrdinationid());

		if (oz != null) {
			return oz;
		}
		// end check if object exists

		// make sure DBenutzer object is already known
		for (i = 0; i < dh.getBenutzer().size() && found == false; i++) {

			if (ocOrdination.getOcBenutzer().getBenutzerid() == dh.getBenutzer()
					.elementAt(i).getBenutzerid()) {
				benutzer = dh.getBenutzer().elementAt(i);
				found = true;
			}

		}

		if (!found) {
			throw new BenutzerNotFoundException();
		}

		anfang = ocOrdination.getOrdinationanfangszeit();
		ende = ocOrdination.getOrdinationendzeit();
		oz = new DOrdinationsZeit(ocOrdination.getOrdinationid(), benutzer,
				ocOrdination.getOrdinationwochentag(), anfang, ende);
		dh.getOrdZeiten().add(oz);

		return oz;
	}


	/**
	 * Creates all DOrdinationsZeit domain objects from the corresponding data
	 * objects.
	 * 
	 * @return a Vector containing all the domain objects.
	 * @throws BenutzerNotFoundException
	 *             if the corresponding DBenutzer object is not found in the
	 *             m_benutzer field of this class.
	 */
	private Vector<DOrdinationsZeit> getAllDOrdZeit()
			throws BenutzerNotFoundException {
		m_ordinationDAO.start();
		List<OcOrdination> oz = m_ordinationDAO.findAll();
		Iterator<OcOrdination> iter = oz.iterator();

		while (iter.hasNext()) {
			OcOrdination coz = iter.next();
			getDOrdZeit(coz);
		}

		return dh.getOrdZeiten();
	}


	/**
	 * Creates all DOrdinationsZeit domain objects from the corresponding data
	 * objects, and returns the ones belonging to the specified doctor.
	 * 
	 * @param arztId
	 *            The doctor, whose working times are required.
	 * @return The working times of the specified doctor.
	 * @throws BenutzerNotFoundException
	 *             if the corresponding DBenutzer object is not found in the
	 *             m_benutzer field of this class.
	 */
	@SuppressWarnings("unused")
	private Vector<DOrdinationsZeit> getAllDOrdZeit(int arztId)
			throws BenutzerNotFoundException {
		getAllDOrdZeit();
		Iterator<DOrdinationsZeit> iter = dh.getOrdZeiten().iterator();
		Vector<DOrdinationsZeit> ordzeiten = new Vector<DOrdinationsZeit>();

		while (iter.hasNext()) {
			DOrdinationsZeit coz = iter.next();

			// only get times for the specified doctor
			if (coz.getiBenutzer().getBenutzerid() == arztId) {
				ordzeiten.add(coz);
			}

		}

		return ordzeiten;
	}


	/**
	 * return a domain arzt from a given arztid
	 * 
	 * @param id
	 * @return
	 * @throws NoArztException
	 * @throws BenutzerNotFoundException
	 */
	public DArzt getDArzt(int id) throws NoArztException,
			BenutzerNotFoundException {
		DArzt cached = getArzt(id);
		if (cached != null) {
		    log.debug("restorted doctor from cache");
			return cached;
		}

		m_benutzerDAO.start();
		OcBenutzer b = m_benutzerDAO.findById(id);

		if (b == null) {
			throw new BenutzerNotFoundException();
		}

		return getDArzt(b);
	}


	/**
	 * Creates a DArzt domain object from data object. This method only creates
	 * references to the user group, the superclass (DBenutzer), the working
	 * times and the times of absence. All other references must be created
	 * after this method is used.
	 * 
	 * @param ocBenutzer
	 * @return
	 */
	public DArzt getDArztWithoutReferences(OcBenutzer ocBenutzer) {

		// check if domain object already exists
		DArzt arzt = getArzt(ocBenutzer.getBenutzerid());

		if (arzt != null) {
			arzt.setKalender(DomainHandler.te.getDTerminKalender(arzt));
			return arzt;
		}
		// end check if object exists

		int i = 0;
		DBenutzer benutzer = null;
		Vector<DOrdinationsZeit> ordZeiten = new Vector<DOrdinationsZeit>();
		Vector<DFehlzeit> fehlZeiten = new Vector<DFehlzeit>();
		// create all groups
		dh.setGruppen(getAllDGruppen());
		// create all users
		dh.setBenutzer(getAllDBenutzer());

		// find this user in list
		while (i < dh.getBenutzer().size() && benutzer == null) {
			if (dh.getBenutzer().elementAt(i).getBenutzerid() == ocBenutzer
					.getBenutzerid()) {
				benutzer = dh.getBenutzer().elementAt(i);
			}

			i++;
		}

		if (benutzer == null) {
			throw new BenutzerNotFoundException();
		} else {

			// make sure that this is a doctor
			if (benutzer.getGruppe().getGruppetyp() == BenutzerDAO.typArzt) {

				// create all working times
				dh.setOrdZeiten(getAllDOrdZeit());

				// find this doctors working times
				i = 0;
				int j = 0;
				while (i < dh.getOrdZeiten().size()) {

					if (dh.getOrdZeiten().elementAt(i).getiBenutzer().getBenutzerid() == benutzer
							.getBenutzerid()) {
						ordZeiten.add(dh.getOrdZeiten().elementAt(i));
						j++;
					}

					i++;
				}

				// make a list for times of absence
				dh.setFehlzeiten(getAllDFehlzeit());

				// find this doctors times of absence
				i = 0;
				while (i < dh.getFehlzeiten().size()) {

					if (dh.getFehlzeiten().elementAt(i).getiBenutzer().equals(
							benutzer)) {
						fehlZeiten.add(dh.getFehlzeiten().elementAt(i));
					}

					i++;
				}
			} else {
				throw new NoArztException();
			}
		}

		// create a new domain object
		DArzt dArzt = new DArzt(ocBenutzer.getBenutzerid(), benutzer
				.getGruppe(), benutzer.getBenutzerusername(), benutzer
				.getBenutzerpasswort(), benutzer.getBenutzervorname(), benutzer
				.getBenutzernachname(), benutzer.getBenutzerstatus(),
				ordZeiten, fehlZeiten);
		
		// register
        dh.getAerzte().add(dArzt);
		
		return dArzt;
	}


	/**
	 * Primary method for creating a DArzt domain object from the corresponding
	 * OcBenutzer data object.
	 * 
	 * @param ocBenutzer
	 * @return A DArzt domain object.
	 * @throws NoArztException
	 *             if the OcBenutzer object does not belong to group of doctors.
	 * @throws BenutzerNotFoundException
	 *            7 if the corresponding DBenutzer object is not found.
	 */
	public DArzt getDArzt(OcBenutzer ocBenutzer) throws NoArztException,
			BenutzerNotFoundException {

		DArzt dArzt = getDArztWithoutReferences(ocBenutzer);
		
		// TODO: get treatments of the doctor

		// make sure that all patients of this doctor exist
		Set<IPatient> patienten = DomainHandler.pe.getDPatienten(dArzt);
		dArzt.setiPatientsForArztid(patienten);

		// calendar filling
		dArzt.setKalender(DomainHandler.te.getDTerminKalender(dArzt));

		return dArzt;
	}





	// private search methods to find already existing domain objects

	private DBenutzerGruppe getGruppe(int id) {

		for (DBenutzerGruppe gruppe : dh.getGruppen()) {

			if (gruppe.getId() == id) {
				return gruppe;
			}

		}

		return null;
	}


	/**
	 * cache lookup
	 * 
	 * @param id
	 * @return
	 */
	public DOrdinationsZeit getOrdZeit(int id) {
		for (DOrdinationsZeit oz : dh.getOrdZeiten()) {
			if (oz.getId() == id) {
				return oz;
			}
		}

		return null;
	}


	/**
	 * cache lookup
	 * 
	 * @param arztId
	 * @return
	 * @throws NoArztException
	 * @throws BenutzerNotFoundException
	 */
	private DArzt getArzt(int arztId) throws NoArztException,
			BenutzerNotFoundException {

		for (DArzt arzt : dh.getAerzte()) {
			if (arzt.getBenutzerid() == arztId) {
				return arzt;
			}
		}

		return null;
	}


	/**
	 * cache lookup
	 * 
	 * @param id
	 * @return
	 */
	private DBenutzer getBenutzer(int id) {

		for (DBenutzer ben : dh.getBenutzer()) {

			if (ben.getBenutzerid() == id) {
				return ben;
			}

		}

		return null;
	}


	/**
	 * cache lookup
	 * 
	 * @param id
	 * @return
	 */
	private DFehlzeit getFehlzeit(int id) {
		for (DFehlzeit fz : dh.getFehlzeiten()) {
			if (fz.getFehlzeitId() == id) {
				return fz;
			}
		}

		return null;
	}


	/**
	 * @return all aerzte
	 */
	public List<IArzt> getDAerzte() {
		m_benutzerDAO.start();

		List<IArzt> alleAerzte = new Vector<IArzt>();
		for (OcBenutzer a : m_benutzerDAO.findByGroup(BenutzerDAO.typArzt)) {
			alleAerzte.add(getDArzt(a));
		}

		return alleAerzte;
	}

}
